package com.example.smartwaterapi.service.impl;

import com.example.smartwaterapi.entity.User;
import com.example.smartwaterapi.service.UserService;
import com.example.smartwaterapi.service.WechatOfficialService;
import com.example.smartwaterapi.service.TemplateMessageService;
import com.example.smartwaterapi.config.WechatConfig;
import com.example.smartwaterapi.common.exception.ApiException;
import com.example.smartwaterapi.common.api.ResultCode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import jakarta.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.net.URLEncoder;
import java.io.UnsupportedEncodingException;

/**
 * 微信公众号服务实现类
 */
@Slf4j
@Service
public class WechatOfficialServiceImpl implements WechatOfficialService {

    @Autowired
    private UserService userService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private ObjectMapper objectMapper;
    
    @Autowired
    private WxMpService wxMpService;
    
    @Autowired
    private TemplateMessageService templateMessageService;
    
    @Autowired
    private WechatConfig wechatConfig;

    @Value("${wechat.official.redirect-uri:}")
    private String defaultRedirectUri;

    @PostConstruct
    public void loadWxMpConfigStorages() {
        log.info("加载公众号配置...");
        this.addAccountToRuntime();
    }

    /**
     * 添加账号到当前程序，如首次添加需初始化configStorageMap
     */
    private synchronized void addAccountToRuntime() {
        String appid = wechatConfig.getAppid();
        WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();
        configStorage.setAppId(appid);
        configStorage.setSecret(wechatConfig.getAppSecret());
        
        try {
            wxMpService.addConfigStorage(appid, configStorage);
        } catch (NullPointerException e) {
            log.info("需初始化configStorageMap...");
            Map<String, WxMpConfigStorage> configStorages = new HashMap<>(4);
            configStorages.put(appid, configStorage);
            wxMpService.setMultiConfigStorages(configStorages, appid);
        }
    }

    /**
     * 微信公众号授权登录
     */
    @Override
    @Transactional
    public Map<String, Object> officialLogin(String code) {
        try {
            if (!StringUtils.hasText(code)) {
                log.error("微信公众号登录失败: code参数为空");
                throw new ApiException(ResultCode.VALIDATE_FAILED, "授权码不能为空");
            }

            log.info("开始微信公众号登录流程, code: {}", code);

            // TODO: 调用微信公众号API获取access_token和用户信息
            // 这里需要实现微信公众号的API调用逻辑
            // 暂时使用占位实现
            
            Map<String, Object> result = processOfficialAccountAuth(code);
            log.info("微信公众号登录成功: {}", result);
            
            return result;

        } catch (ApiException e) {
            throw e;
        } catch (Exception e) {
            log.error("微信公众号登录异常", e);
            throw new ApiException(ResultCode.WECHAT_LOGIN_FAILED, "公众号登录失败: " + e.getMessage());
        }
    }

    /**
     * 生成公众号授权URL
     */
    @Override
    public String generateAuthUrl(String redirectUri, String state) {
        try {
            if (!StringUtils.hasText(redirectUri)) {
                redirectUri = defaultRedirectUri;
            }
            
            if (!StringUtils.hasText(state)) {
                state = String.valueOf(System.currentTimeMillis());
            }

            // URL编码回调地址和状态参数
            String encodedRedirectUri = URLEncoder.encode(redirectUri, "UTF-8");
            String encodedState = URLEncoder.encode(state, "UTF-8");
            
            // 微信公众号授权URL格式
            String authUrl = String.format(
                "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_userinfo&state=%s#wechat_redirect",
                wechatConfig.getAppid(), encodedRedirectUri, encodedState
            );
            
            log.info("生成公众号授权URL: appid={}, redirectUri={}, state={}", 
                wechatConfig.getAppid(), redirectUri, state);
            
            return authUrl;
            
        } catch (Exception e) {
            log.error("生成公众号授权URL失败", e);
            throw new ApiException(ResultCode.SYSTEM_ERROR, "生成授权URL失败: " + e.getMessage());
        }
    }

    /**
     * 绑定公众号到现有用户
     */
    @Override
    @Transactional
    public boolean bindOfficialAccount(Long userId, String code) {
        try {
            if (userId == null || !StringUtils.hasText(code)) {
                log.error("绑定公众号参数无效: userId={}, code={}", userId, code);
                return false;
            }

            // 检查用户是否存在
            User user = userService.getById(userId);
            if (user == null) {
                log.error("绑定公众号失败: 用户不存在, userId={}", userId);
                return false;
            }

            // TODO: 通过code获取公众号的openid
            // 这里需要调用微信API
            String wxOpenid = getWxOpenidByCode(code);
            
            if (!StringUtils.hasText(wxOpenid)) {
                log.error("获取公众号openid失败");
                return false;
            }

            // 检查该公众号是否已绑定其他用户
            User existingUser = userService.getByWxOpenid(wxOpenid);
            if (existingUser != null && !existingUser.getId().equals(userId)) {
                log.error("该公众号已绑定其他用户: wxOpenid={}, existingUserId={}", wxOpenid, existingUser.getId());
                return false;
            }

            // 更新用户的公众号openid
            return userService.updateWxOpenid(userId, wxOpenid);

        } catch (Exception e) {
            log.error("绑定公众号异常: userId={}, code={}", userId, code, e);
            return false;
        }
    }

    /**
     * 解绑公众号
     */
    @Override
    @Transactional
    public boolean unbindOfficialAccount(Long userId) {
        try {
            if (userId == null) {
                log.error("解绑公众号失败: 用户ID为空");
                return false;
            }

            // 将公众号openid设为null
            return userService.updateWxOpenid(userId, null);

        } catch (Exception e) {
            log.error("解绑公众号异常: userId={}", userId, e);
            return false;
        }
    }

    /**
     * 通过unionid统一处理openid
     */
    @Override
    @Transactional
    public User unifyUserByUnionid(String unionid, String wxOpenid, String miniOpenid) {
        try {
            if (!StringUtils.hasText(unionid)) {
                log.error("unionid不能为空");
                return null;
            }

            log.info("通过unionid统一用户: unionid={}, wxOpenid={}, miniOpenid={}", unionid, wxOpenid, miniOpenid);

            // 先尝试通过小程序openid查找用户
            User user = null;
            if (StringUtils.hasText(miniOpenid)) {
                user = userService.getByOpenid(miniOpenid);
            }

            // 如果小程序openid找不到，尝试通过公众号openid查找
            if (user == null && StringUtils.hasText(wxOpenid)) {
                user = userService.getByWxOpenid(wxOpenid);
            }

            if (user != null) {
                // 更新用户的unionid和openid信息
                User updateUser = new User();
                updateUser.setId(user.getId());
                updateUser.setUnionid(unionid);
                
                if (StringUtils.hasText(wxOpenid) && !wxOpenid.equals(user.getWxOpenid())) {
                    updateUser.setWxOpenid(wxOpenid);
                }
                
                if (StringUtils.hasText(miniOpenid) && !miniOpenid.equals(user.getOpenid())) {
                    updateUser.setOpenid(miniOpenid);
                }
                
                updateUser.setUpdatedAt(LocalDateTime.now());
                userService.updateById(updateUser);
                
                log.info("统一用户信息更新成功: userId={}", user.getId());
                return userService.getById(user.getId()); // 返回最新信息
            }

            log.info("未找到对应用户，需要创建新用户");
            return null;

        } catch (Exception e) {
            log.error("通过unionid统一用户异常", e);
            return null;
        }
    }

    /**
     * 处理公众号授权
     */
    private Map<String, Object> processOfficialAccountAuth(String code) {
        try {
            // 1. 通过code获取access_token和openid
            Map<String, Object> tokenInfo = getAccessTokenAndOpenid(code);
            String accessToken = (String) tokenInfo.get("access_token");
            String wxOpenid = (String) tokenInfo.get("openid");
            String unionid = (String) tokenInfo.get("unionid");

            if (!StringUtils.hasText(wxOpenid)) {
                throw new ApiException(ResultCode.WECHAT_LOGIN_FAILED, "获取微信openid失败");
            }

            // 2. 获取用户信息
            Map<String, Object> userInfo = getUserInfo(accessToken, wxOpenid);
            
            // 3. 处理用户登录或注册逻辑
            User user = handleOfficialAccountLogin(wxOpenid, unionid, userInfo);

            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("message", "公众号授权成功");
            result.put("wxOpenid", wxOpenid);
            result.put("unionid", unionid);
            result.put("user", user);
            
            return result;

        } catch (ApiException e) {
            throw e;
        } catch (Exception e) {
            log.error("处理公众号授权异常", e);
            throw new ApiException(ResultCode.WECHAT_LOGIN_FAILED, "公众号授权处理失败: " + e.getMessage());
        }
    }

    /**
     * 通过code获取access_token和openid
     */
    private Map<String, Object> getAccessTokenAndOpenid(String code) {
        try {
            String url = String.format(
                "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
                wechatConfig.getAppid(), wechatConfig.getAppSecret(), code
            );

            log.info("获取公众号access_token: {}", url);
            String response = restTemplate.getForObject(url, String.class);
            log.info("微信API响应: {}", response);

            Map<String, Object> result = objectMapper.readValue(response, Map.class);
            
            if (result.containsKey("errcode")) {
                Integer errcode = (Integer) result.get("errcode");
                String errmsg = (String) result.get("errmsg");
                log.error("获取access_token失败: errcode={}, errmsg={}", errcode, errmsg);
                throw new ApiException(ResultCode.WECHAT_LOGIN_FAILED, "获取授权失败: " + errmsg);
            }

            return result;

        } catch (Exception e) {
            log.error("调用微信API获取access_token异常", e);
            throw new ApiException(ResultCode.WECHAT_LOGIN_FAILED, "获取授权信息失败");
        }
    }

    /**
     * 获取用户信息
     */
    private Map<String, Object> getUserInfo(String accessToken, String openid) {
        try {
            String url = String.format(
                "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN",
                accessToken, openid
            );

            log.info("获取用户信息: openid={}", openid);
            String response = restTemplate.getForObject(url, String.class);
            
            Map<String, Object> userInfo = objectMapper.readValue(response, Map.class);
            
            if (userInfo.containsKey("errcode")) {
                Integer errcode = (Integer) userInfo.get("errcode");
                String errmsg = (String) userInfo.get("errmsg");
                log.error("获取用户信息失败: errcode={}, errmsg={}", errcode, errmsg);
                throw new ApiException(ResultCode.WECHAT_LOGIN_FAILED, "获取用户信息失败: " + errmsg);
            }

            return userInfo;

        } catch (Exception e) {
            log.error("获取用户信息异常", e);
            throw new ApiException(ResultCode.WECHAT_LOGIN_FAILED, "获取用户信息失败");
        }
    }

    /**
     * 处理公众号登录
     */
    private User handleOfficialAccountLogin(String wxOpenid, String unionid, Map<String, Object> userInfo) {
        try {
            // 先通过公众号openid查找用户
            User user = userService.getByWxOpenid(wxOpenid);
            
            if (user == null && StringUtils.hasText(unionid)) {
                // 如果通过openid找不到，尝试通过unionid查找（可能用户之前通过小程序注册）
                user = userService.getByUnionid(unionid);
                if (user != null) {
                    // 更新用户的公众号openid
                    userService.updateWxOpenid(user.getId(), wxOpenid);
                    log.info("通过unionid找到用户并更新公众号openid: userId={}, wxOpenid={}", user.getId(), wxOpenid);
                }
            }

            if (user == null) {
                // 创建新用户
                user = createUserFromOfficialAccount(wxOpenid, unionid, userInfo);
                log.info("创建新的公众号用户: userId={}, wxOpenid={}", user.getId(), wxOpenid);
            } else {
                // 更新用户信息
                updateUserFromOfficialAccount(user, unionid, userInfo);
                log.info("更新公众号用户信息: userId={}", user.getId());
            }

            return user;

        } catch (Exception e) {
            log.error("处理公众号登录异常: wxOpenid={}, unionid={}", wxOpenid, unionid, e);
            throw new ApiException(ResultCode.WECHAT_LOGIN_FAILED, "用户登录处理失败");
        }
    }

    /**
     * 从公众号信息创建新用户
     */
    private User createUserFromOfficialAccount(String wxOpenid, String unionid, Map<String, Object> userInfo) {
        User user = new User();
        user.setWxOpenid(wxOpenid);
        user.setUnionid(unionid);
        
        if (userInfo != null) {
            user.setNickname((String) userInfo.get("nickname"));
            user.setAvatar((String) userInfo.get("headimgurl"));
            
            // 安全处理性别字段的类型转换
            Object sexObj = userInfo.get("sex");
            Integer sex = null;
            if (sexObj instanceof Integer) {
                sex = (Integer) sexObj;
            } else if (sexObj instanceof String) {
                try {
                    sex = Integer.parseInt((String) sexObj);
                } catch (NumberFormatException e) {
                    log.warn("无法解析性别字段: {}", sexObj);
                }
            }
            user.setGender(sex);
        }
        
        user.setCreatedAt(LocalDateTime.now());
        user.setUpdatedAt(LocalDateTime.now());
        user.setLastLoginTime(LocalDateTime.now());
        
        boolean success = userService.save(user);
        if (!success) {
            throw new ApiException(ResultCode.WECHAT_LOGIN_FAILED, "创建用户失败");
        }
        
        return user;
    }

    /**
     * 更新用户的公众号信息
     */
    private void updateUserFromOfficialAccount(User user, String unionid, Map<String, Object> userInfo) {
        User updateUser = new User();
        updateUser.setId(user.getId());
        updateUser.setUpdatedAt(LocalDateTime.now());
        updateUser.setLastLoginTime(LocalDateTime.now());
        
        if (StringUtils.hasText(unionid) && !unionid.equals(user.getUnionid())) {
            updateUser.setUnionid(unionid);
        }
        
        if (userInfo != null) {
            String nickname = (String) userInfo.get("nickname");
            String avatar = (String) userInfo.get("headimgurl");
            
            // 安全处理性别字段的类型转换
            Object sexObj = userInfo.get("sex");
            Integer sex = null;
            if (sexObj instanceof Integer) {
                sex = (Integer) sexObj;
            } else if (sexObj instanceof String) {
                try {
                    sex = Integer.parseInt((String) sexObj);
                } catch (NumberFormatException e) {
                    log.warn("无法解析性别字段: {}", sexObj);
                }
            }
            
            if (StringUtils.hasText(nickname) && !nickname.equals(user.getNickname())) {
                updateUser.setNickname(nickname);
            }
            if (StringUtils.hasText(avatar) && !avatar.equals(user.getAvatar())) {
                updateUser.setAvatar(avatar);
            }
            if (sex != null && !sex.equals(user.getGender())) {
                updateUser.setGender(sex);
            }
        }
        
        userService.updateById(updateUser);
    }


    /**
     * 通过code获取公众号openid
     */
    private String getWxOpenidByCode(String code) {
        try {
            log.info("通过code获取公众号openid: code={}", code);
            Map<String, Object> tokenInfo = getAccessTokenAndOpenid(code);
            
            if (tokenInfo != null && tokenInfo.containsKey("openid")) {
                String openid = (String) tokenInfo.get("openid");
                log.info("成功获取公众号openid: {}", openid != null ? openid.substring(0, Math.min(8, openid.length())) + "***" : "null");
                return openid;
            } else {
                log.error("获取公众号openid失败: tokenInfo={}", tokenInfo);
                return null;
            }
        } catch (Exception e) {
            log.error("通过code获取openid失败", e);
            return null;
        }
    }

    /**
     * 获取微信公众号Access Token
     */
    public String getWechatAccessToken() {
        try {
            String url = String.format(
                "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
                wechatConfig.getAppid(), wechatConfig.getAppSecret()
            );

            String response = restTemplate.getForObject(url, String.class);
            Map<String, Object> result = objectMapper.readValue(response, Map.class);
            
            if (result.containsKey("access_token")) {
                return (String) result.get("access_token");
            } else {
                log.error("获取AccessToken失败: {}", result);
                return null;
            }
        } catch (Exception e) {
            log.error("获取AccessToken异常", e);
            return null;
        }
    }
} 